using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using TallComponents.Web.Pdf.Resources;

[assembly: WebResource( TallComponents.Web.Pdf.PdfThumbnailsList.ScriptResourcePath, "text/javascript" )]
[assembly: WebResource( TallComponents.Web.Pdf.PdfThumbnailsList.EmptyPageImagePath, "image/png" )]

namespace TallComponents.Web.Pdf
{
   /// <summary>
   /// Renders a list of thumbnails.
   /// </summary>
   [System.Drawing.ToolboxBitmap( typeof( PdfThumbnailsList ), IconPath )]
   [ToolboxItem( "System.Web.UI.Design.WebControlToolboxItem, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" )]
   public class PdfThumbnailsList : ScriptControl
   {
      /// <summary>
      /// Initializes a new instance of the <see cref="PdfThumbnailsList"/> class.
      /// </summary>
      public PdfThumbnailsList()
      {
         Orientation = Orientation.Vertical;
      }

      internal const string IconPath = "TallComponents.Web.Pdf.PdfWebViewer.ico";

      // This is the resource path to the javascript source file
      // Note that resource paths always start with the assembly default namespace
#if DEBUG
      internal const string ScriptResourcePath = "TallComponents.Web.Pdf.PdfThumbnailsList.js";
#else
      internal const string ScriptResourcePath = "TallComponents.Web.Pdf.PdfThumbnailsList.min.js";
#endif

      internal const string ClientControlType = "TallComponents.ThumbnailsList";

      internal const string EmptyPageImagePath = "TallComponents.Web.Pdf.white.png";

      /// <summary>
      /// Raises the <see cref="E:System.Web.UI.Control.PreRender"/> event.
      /// </summary>
      /// <param name="e">An <see cref="T:System.EventArgs"/> object that contains the event data.</param>
      protected override void OnPreRender( EventArgs e )
      {
         base.OnPreRender( e );

         if ( string.IsNullOrEmpty( PdfViewerControlId ) )
            throw new InvalidOperationException( Text.PdfViewerControlId_not_set );

         var targetControl = FindTargetControl( PdfViewerControlId ) as PdfViewer;

         if ( null == targetControl )
            throw new InvalidOperationException( string.Format( Text.ViewerControl_not_found, PdfViewerControlId ) );

         _pageViewChildControlId = targetControl.ClientID;

         _storageKey = targetControl.StorageKey;

         if ( string.IsNullOrEmpty( ServiceUrl ) )
            throw new HttpException( Text.Thumbnails_ServiceUrl_not_set );

         var listStyle = new CssStyle();
         listStyle.Add( "margin", "0px" );
         listStyle.Add( "padding", "0px" );
         //listStyle.Add( "height", "inherit" );
         if ( Orientation == Orientation.Horizontal )
            listStyle.Add( "white-space", "nowrap" );
         Page.Header.StyleSheet.CreateStyleRule( listStyle, this, string.Format( "#{0} ul", ClientID ) );

         var listItemStyle = new CssStyle();
         listItemStyle.Add( "margin", "0px" );
         listItemStyle.Add( "padding", "0px" );
         listItemStyle.Add( "list-style-position", "outside" );
         listItemStyle.Add( "list-style-type", "none" );
         if ( Orientation == Orientation.Horizontal )
         {
            // browser dependent styles...
            bool inlineBlockNotSupported =
               ( Context.Request.Browser.Browser.Equals( "IE" ) && ( Context.Request.Browser.MajorVersion < 8 ) ) ||
               ( Context.Request.Browser.Browser.Equals( "Firefox" ) && ( Context.Request.Browser.MajorVersion < 3 ) );

            // display:inline causes IE8 to slow down. inline-block is supported on all new browsers.
            listItemStyle.Add( "display", inlineBlockNotSupported ? "inline" : "inline-block" );
         }

         Page.Header.StyleSheet.CreateStyleRule( listItemStyle, this, string.Format( "#{0} li", ClientID ) );
      }

      /// <summary>
      /// Renders the control to the specified HTML writer.
      /// </summary>
      /// <param name="writer">The <see cref="T:System.Web.UI.HtmlTextWriter"/> object that receives the control content.</param>
      protected override void Render( HtmlTextWriter writer )
      {
         AddAttributesToRender( writer );
         writer.AddStyleAttribute( HtmlTextWriterStyle.TextAlign, "center" );
         writer.AddStyleAttribute( HtmlTextWriterStyle.Overflow, "scroll" );
         writer.AddStyleAttribute( HtmlTextWriterStyle.OverflowX, Orientation == Orientation.Horizontal ? "scroll" : "hidden" );
         writer.AddStyleAttribute( HtmlTextWriterStyle.OverflowY, Orientation == Orientation.Vertical ? "scroll" : "hidden" );
         writer.RenderBeginTag( HtmlTextWriterTag.Div );
         writer.RenderBeginTag( HtmlTextWriterTag.Ul );

         if ( !DesignMode )
         {
            if ( !string.IsNullOrEmpty( _storageKey ) )
            {
               Document doc = Storage.GetDocument( _storageKey );
               if ( null != doc )
               {
                  int pageIndex = 0;
                  foreach( var page in doc.Pages )
                  {
                     double thumbScale = scale;
                     if ( Orientation == Orientation.Horizontal && !Height.IsEmpty && Height.Type == UnitType.Pixel )
                     {
                        thumbScale = Math.Min( scale, ( Height.Value - 26 ) / page.Height );
                     }
                     else if ( Orientation == Orientation.Vertical && !Width.IsEmpty && Width.Type == UnitType.Pixel )
                     {
                        thumbScale = Math.Min( scale, ( Width.Value - 26 ) / page.Width );                        
                     }

                     RenderThumbnailMarkup( writer,
                        string.Format( Text.Thumbnails_Page_nr, pageIndex + 1 ),
                        ( int )( page.Width * thumbScale ),
                        ( int )( page.Height * thumbScale ),
                        HttpUtility.HtmlAttributeEncode( Page.ClientScript.GetWebResourceUrl( typeof( PdfThumbnailsList ), EmptyPageImagePath ) ),
                        string.Format( "$find('{0}').set_pageIndex( {1} );return false;", _pageViewChildControlId, pageIndex ) );

                     pageIndex++;
                  }
               }
            }
         }

         writer.RenderEndTag(); // ul
         writer.RenderEndTag(); // div

         if ( !DesignMode )
         {
            ScriptManager.RegisterScriptDescriptors( this );
         }
      }

      private void RenderThumbnailMarkup( HtmlTextWriter writer, string title, int width, int height, string imageUrl, string onClick )
      {
         writer.RenderBeginTag( HtmlTextWriterTag.Li );
         if ( null != _thumbnailStyle )
            _thumbnailStyle.AddAttributesToRender( writer );

         writer.AddAttribute( HtmlTextWriterAttribute.Href, "#" );
         writer.AddAttribute( HtmlTextWriterAttribute.Onclick, onClick );
         writer.RenderBeginTag( HtmlTextWriterTag.A );
         writer.AddAttribute( HtmlTextWriterAttribute.Width, width.ToString() );
         writer.AddAttribute( HtmlTextWriterAttribute.Height, height.ToString() );
         writer.AddAttribute( HtmlTextWriterAttribute.Title, title );
         writer.AddAttribute( HtmlTextWriterAttribute.Alt, title );
         writer.AddAttribute( HtmlTextWriterAttribute.Src, imageUrl );
         writer.RenderBeginTag( HtmlTextWriterTag.Img );
         writer.RenderEndTag();

         writer.RenderEndTag(); //a
         writer.RenderEndTag(); //li
      }

      /// <summary>
      /// Searches the control hierarchy to locate the named control.
      /// </summary>
      /// <param name="id">The control id of the control to look for.</param>
      /// <returns>The control with the specified <paramref name="id"/> or <c>null</c> if no matching control is found.</returns>
      /// /// <exception cref="ArgumentException">Occures when <paramref name="id"/> is <c>null</c> or empty.</exception>
      protected Control FindTargetControl( string id )
      {
         if ( string.IsNullOrEmpty( "id" ) )
            throw new ArgumentException( "id" );

         Control ctrl = base.FindControl( id ); 
         Control namingContainer = NamingContainer;

         // Walk up into the control tree to locate the target control
         while ( ( null == ctrl ) && ( null != namingContainer ) )
         {
            ctrl = namingContainer.FindControl( id );
            namingContainer = namingContainer.NamingContainer;
         }

         return ctrl;
      }

      /// <summary>
      /// Gets or sets the URL for the thumbnail generator.
      /// </summary>
      /// <value>The URL for the thumbnail generator.</value>
      [UrlProperty]
      [Editor( "System.Web.UI.Design.UrlEditor", typeof( System.Drawing.Design.UITypeEditor ) )]
      public string ServiceUrl
      {
         get { return _serviceUrl; }
         set { _serviceUrl = value; }
      }

      /// <summary>
      /// Gets or sets the page view control id.
      /// </summary>
      /// <value>The page view control id.</value>
      [IDReferenceProperty(typeof(PdfViewer))]
      public string PdfViewerControlId
      {
         get { return _pageViewControlId; }
         set { _pageViewControlId = value; }
      }

      /// <summary>
      /// Gets the thumbnail style.
      /// </summary>
      /// <value>The thumbnail style.</value>
      private Style ThumbnailStyle
      {
         get
         {
            if ( null == _thumbnailStyle )
               _thumbnailStyle = new Style();
            return _thumbnailStyle;
         }
      }

      /// <summary>
      /// Gets or sets the direction of the list; either vertical (default) or horizontal.
      /// </summary>
      /// <value>The orientation for the list.</value>
      public Orientation Orientation { get; set; }

      /// <summary>
      /// Gets or sets the CSS class for the thumbnail that corresponds to the active page
      /// in the associated PdfViewer.
      /// </summary>
      /// <value>The thumbnail active CSS class.</value>
      public string SelectedThumbnailCssClass { get; set; }

      /// <summary>
      /// Gets or sets the CSS class applied to all thumbnails.
      /// </summary>
      /// <value>The thumbnail CSS class.</value>
      public string ThumbnailCssClass
      {
         get { return ThumbnailStyle.CssClass; }
         set { ThumbnailStyle.CssClass = value; }
      }

      /// <summary>
      /// Gets the script manager for the current request.
      /// </summary>
      /// <value>The script manager for the current request.</value>
      private ScriptManager ScriptManager
      {
         get
         {
            if ( _scriptManager == null )
            {
               if ( Page == null )
               {
                  throw new InvalidOperationException( Text.Common_PageCannotBeNull );
               }
               _scriptManager = ScriptManager.GetCurrent( Page );
               if ( _scriptManager == null )
               {
                  throw new InvalidOperationException( string.Format( CultureInfo.InvariantCulture, Text.Common_ScriptManagerRequired, ID ) );
               }
            }
            return _scriptManager;
         }
      }

      /// <summary>
      /// Gets the PDF storage.
      /// </summary>
      /// <value>The PDF storage.</value>
      private TallComponents.Web.Storage.IPdfStorage Storage
      {
         get
         {
            if ( _storage == null )
            {
               _storage = TallComponents.Web.Storage.PdfStorage.Provider;
            }
            return _storage;
         }
      }

      /// <summary>
      /// Returns the <see cref="T:System.Web.UI.ScriptDescriptor"/> objects for the control.
      /// </summary>
      /// <returns>
      /// An <see cref="T:System.Collections.IEnumerable"/> collection of <see cref="T:System.Web.UI.ScriptDescriptor"/> objects.
      /// </returns>
      protected override IEnumerable<ScriptDescriptor> GetScriptDescriptors()
      {
         var desc = new ScriptControlDescriptor( ClientControlType, ClientID );
         string url = PdfToImageHandler.GetThumbnailTemplateUrl( ServiceUrl );
         desc.AddProperty( "autoScaleMode", Orientation.ToString().ToLower() );
         desc.AddProperty( "baseUrl", url );
         desc.AddProperty( "scale", scale );
         desc.AddProperty( "targetControlId", _pageViewChildControlId );
         if( !string.IsNullOrEmpty( ThumbnailCssClass ) )
            desc.AddProperty( "activeCssClass", SelectedThumbnailCssClass );
         yield return desc;
      }

      /// <summary>
      /// Returns the script files for the control.
      /// </summary>
      /// <returns>
      /// An <see cref="T:System.Collections.IEnumerable"/> collection that contains ECMAScript (JavaScript) files that have been registered as embedded resources.
      /// </returns>
      protected override IEnumerable<ScriptReference> GetScriptReferences()
      {
         var scriptReferences = new List<ScriptReference> {
            new ScriptReference( ScriptResourcePath, "TallComponents.Web.PdfViewer" )
         };
         return scriptReferences;
      }

      private TallComponents.Web.Storage.IPdfStorage _storage;
      private ScriptManager _scriptManager;

      Style _thumbnailStyle;
      string _pageViewControlId;
      string _pageViewChildControlId;
      string _storageKey;
      double scale = 0.25;
      string _serviceUrl = null;
   }
}
